/*
 * public.c
 *
 *  Created on: 2018年10月16日
 *  Author: Degang.Wu (windragon@126.com)
 *  Desc:
 */



#include <sched.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <errno.h>
#include "public.h"
#include "enc_eth.h"

static int g_enable_master = 0;		// default is slave

/*
 * flag : 	1-- set to master
 * 			0-- set to slave
 */
void  enc_set_master(int flag)
{
	g_enable_master = !!flag;
}

int enc_is_master()
{
	return g_enable_master;
}

static void enc_set_scheduler(int policy, int priority)
{
	//SCHED_OTHER 0
	//SCHED_FIFO 1
	//SCHED_RR 2
	struct sched_param param;
	param.sched_priority = priority;
	sched_setscheduler(getpid(),policy, &param);
}

void enc_set_defscheduler(void)
{
	enc_set_scheduler(SCHED_OTHER,0);
}

void enc_set_rtlscheduler(void)
{
	enc_set_scheduler(SCHED_RR,sched_get_priority_max(SCHED_RR));
}

void enc_gettime(struct timeval* tv)
{
	struct timespec			curTime;

	clock_gettime(CLOCK_MONOTONIC,&curTime);

	tv->tv_sec = curTime.tv_sec;
	tv->tv_usec = curTime.tv_nsec/1000;

}

U64 enc_gettime_ms()
{
	struct timespec			curTime;

	clock_gettime(CLOCK_MONOTONIC,&curTime);

	return (U64)curTime.tv_sec * 1000 + curTime.tv_nsec/1000000;
}


int enc_open_rcvsocket(pENC_ETHIOSET piodesc)
{
	int                   socket_fd = -1;
	struct sockaddr_in*   socket_host;
	struct sockaddr_in    socket_sin;
	struct ifreq          socket_ifreq;
	struct ip_mreq        socket_mreq;
	struct ip_mreq_source socket_mreq_src;
	struct in_addr        socket_localaddr;

	int                   flag = 1;
	int                   rcvbufsize = 512*1024;
	struct timeval        timeout;

	char                  localname[50];
	char                  sipaddr_s[50];
	char                  dipaddr_s[50];
	unsigned int          sipaddr = piodesc->ethio_sipaddr;
	unsigned int          dipaddr = piodesc->ethio_dipaddr;
	unsigned short        sipport = piodesc->ethio_sipport;
	unsigned short        dipport = piodesc->ethio_dipport;
	unsigned int          ethid   = piodesc->ethio_ethid;

	// 20180919 degang.wu only debug
	ethid = enc_get_map_ethid(ethid);

	if(piodesc->ethio_isenable==0) return -1;

	int is_master = enc_is_master();

	snprintf(sipaddr_s,sizeof(sipaddr_s),"%u.%u.%u.%u",(sipaddr>>24),(sipaddr>>16)&0xff,(sipaddr>>8)&0xff,(sipaddr&0xff));
	snprintf(dipaddr_s,sizeof(dipaddr_s),"%u.%u.%u.%u",(dipaddr>>24),(dipaddr>>16)&0xff,(dipaddr>>8)&0xff,(dipaddr&0xff));
	if (is_master)
	{
		snprintf(localname,sizeof(localname),"%s",(ethid<g_encset_mst->enc_ethdevnum) ? (char*)g_encset_mst->enc_ethdevname[ethid] : "localhost");
	}
	else
	{
		snprintf(localname,sizeof(localname),"%s",(ethid<g_encset_slv->enc_ethdevnum) ? (char*)g_encset_slv->enc_ethdevname[ethid] : "localhost");
	}

	printf("===========>open rcv socket, sipaddr = %s, sport = %d, dipaddr = %s, dport = %d, localeth = %s\n",sipaddr_s,sipport,dipaddr_s,dipport,localname);

	bzero(&socket_ifreq,sizeof(struct ifreq));
	snprintf(socket_ifreq.ifr_name,sizeof(socket_ifreq.ifr_name),"%s", localname);

	if((socket_fd=socket(AF_INET,SOCK_DGRAM,0))==-1)
	{
		return -1;
	}

	if(ioctl(socket_fd, SIOCGIFADDR, &socket_ifreq)==-1)
	{
		close(socket_fd);
		return -1;
	}

	if(setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag))<0)
	{
		close(socket_fd);
		return -1;
	}

	//SO_REUSEPORT could not support for arm/hisi
//	if(setsockopt(socket_fd,SOL_SOCKET,SO_REUSEPORT,&flag,sizeof(flag))<0)
//	{
//		close(socket_fd);
//		return -1;
//	}

	if(setsockopt(socket_fd,SOL_SOCKET,SO_RCVBUF,&rcvbufsize,sizeof(rcvbufsize))<0)
	{
		close(socket_fd);
		return -1;
	}

	timeout.tv_sec = 2;		//@@ 1秒
	timeout.tv_usec = 0;
	if(setsockopt(socket_fd,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout))<0)
	{
		close(socket_fd);
		return -1;
	}

	bzero(&socket_sin,sizeof(socket_sin));
	socket_sin.sin_family = AF_INET;
	socket_sin.sin_addr.s_addr = inet_addr(dipaddr_s);
	socket_sin.sin_port = htons(dipport);

	if(bind(socket_fd,(struct sockaddr*)&socket_sin,sizeof(socket_sin))<0)
	{
		close(socket_fd);
		return -1;
	}

    socket_host =  (struct sockaddr_in*)&socket_ifreq.ifr_addr;

	socket_mreq.imr_multiaddr.s_addr = inet_addr(dipaddr_s);
	socket_mreq.imr_interface.s_addr = socket_host->sin_addr.s_addr;

	socket_mreq_src.imr_sourceaddr.s_addr = inet_addr(sipaddr_s);
	socket_mreq_src.imr_multiaddr.s_addr  = inet_addr(dipaddr_s);
	socket_mreq_src.imr_interface.s_addr  = socket_host->sin_addr.s_addr;

	socket_localaddr.s_addr = socket_host->sin_addr.s_addr;

	if((socket_mreq.imr_multiaddr.s_addr&0x000000e0)==0x000000e0)
	{
		if(setsockopt(socket_fd,IPPROTO_IP,IP_MULTICAST_IF,(char*)&socket_localaddr,sizeof(socket_localaddr))<0)
		{
			close(socket_fd);
			return -1;
		}

		if(setsockopt(socket_fd, IPPROTO_IP, sipaddr?IP_ADD_SOURCE_MEMBERSHIP:IP_ADD_MEMBERSHIP, sipaddr?((const char*)&socket_mreq_src):((const char*)&socket_mreq), sipaddr?sizeof(socket_mreq_src):sizeof(socket_mreq))<0)
		{
			close(socket_fd);
			return -1;
		}
	}

	return socket_fd;
}


void enc_add_group(int socket_fd, pENC_ETHIOSET piodesc)
{
	struct sockaddr_in* socket_host;
	struct ifreq socket_ifreq;
	struct ip_mreq socket_mreq;
	struct ip_mreq_source socket_mreq_src;
	struct in_addr socket_localaddr;

	char localname[50];
	char sipaddr_s[50];
	char dipaddr_s[50];
	unsigned int sipaddr = piodesc->ethio_sipaddr;
	unsigned int dipaddr = piodesc->ethio_dipaddr;
	unsigned int ethid = piodesc->ethio_ethid;

	// 20180919 degang.wu only debug
	ethid = enc_get_map_ethid(ethid);
	// 20180919 degang.wu only debug

	int is_master = enc_is_master();

	if (socket_fd < 0 || piodesc->ethio_isenable == 0)
	{
		return;
	}

	snprintf(sipaddr_s, sizeof(sipaddr_s), "%u.%u.%u.%u", (sipaddr >> 24),
			(sipaddr >> 16) & 0xff, (sipaddr >> 8) & 0xff, (sipaddr & 0xff));
	snprintf(dipaddr_s, sizeof(dipaddr_s), "%u.%u.%u.%u", (dipaddr >> 24),
			(dipaddr >> 16) & 0xff, (dipaddr >> 8) & 0xff, (dipaddr & 0xff));
	if (is_master)
	{
		snprintf(localname, sizeof(localname), "%s",
				(ethid < g_encset_mst->enc_ethdevnum) ?
						(char*) g_encset_mst->enc_ethdevname[ethid] : "localhost");
	}
	else
	{
		snprintf(localname, sizeof(localname), "%s",
				(ethid < g_encset_slv->enc_ethdevnum) ?
						(char*) g_encset_slv->enc_ethdevname[ethid] : "localhost");
	}

	bzero(&socket_ifreq, sizeof(struct ifreq));
	snprintf(socket_ifreq.ifr_name, sizeof(socket_ifreq.ifr_name), "%s", localname);

	if (ioctl(socket_fd, SIOCGIFADDR, &socket_ifreq) == -1)
		return;

	socket_host = (struct sockaddr_in*) &socket_ifreq.ifr_addr;

	socket_mreq.imr_multiaddr.s_addr = inet_addr(dipaddr_s);
	socket_mreq.imr_interface.s_addr = socket_host->sin_addr.s_addr;

	socket_mreq_src.imr_sourceaddr.s_addr = inet_addr(sipaddr_s);
	socket_mreq_src.imr_multiaddr.s_addr = inet_addr(dipaddr_s);
	socket_mreq_src.imr_interface.s_addr = socket_host->sin_addr.s_addr;

	socket_localaddr.s_addr = socket_host->sin_addr.s_addr;

	if ((socket_mreq.imr_multiaddr.s_addr & 0x000000e0) == 0x000000e0)
	{
		setsockopt(socket_fd, IPPROTO_IP, IP_MULTICAST_IF,
				(char*) &socket_localaddr, sizeof(socket_localaddr));

		setsockopt(socket_fd, IPPROTO_IP,
				sipaddr ? IP_DROP_SOURCE_MEMBERSHIP : IP_DROP_MEMBERSHIP,
				sipaddr ? ((const char*) &socket_mreq_src) : ((const char*) &socket_mreq),
				sipaddr ? sizeof(socket_mreq_src) : sizeof(socket_mreq));

		setsockopt(socket_fd, IPPROTO_IP,
				sipaddr ? IP_ADD_SOURCE_MEMBERSHIP : IP_ADD_MEMBERSHIP,
				sipaddr ? ((const char*) &socket_mreq_src) : ((const char*) &socket_mreq),
				sipaddr ? sizeof(socket_mreq_src) : sizeof(socket_mreq));
	}
}

int enc_open_sndsocket(pENC_ETHIOSET piodesc, const char* netname)
{
	int socket_fd = -1;
	int flag = 1;
	int sndbufsize = 0;		//1024*1024*1;
	struct ifreq socket_ifreq;
//	struct sockaddr_in* socket_host;
//	struct sockaddr_in  socket_sin;
	char localname[20];
	unsigned char ttl = 255;
//	char sipaddr_s[50];
//	unsigned int sipaddr = piodesc->ethio_sipaddr;
	unsigned int ethid = piodesc->ethio_ethid;

	// 20180919 degang.wu only debug
	ethid = enc_get_map_ethid(ethid);
	// 20180919 degang.wu only debug

	int is_master = enc_is_master();

	if (piodesc->ethio_isenable == 0 || piodesc->ethio_dipaddr == 0)
		return -1;

	if (NULL == netname)
	{
		if (is_master)
		{
			snprintf(localname, sizeof(localname), "%s",
					(ethid < g_encset_mst->enc_ethdevnum) ?
					(char*) g_encset_mst->enc_ethdevname[ethid] : "localhost");
		}
		else
		{
			snprintf(localname, sizeof(localname), "%s",
					(ethid < g_encset_slv->enc_ethdevnum) ?
					(char*) g_encset_slv->enc_ethdevname[ethid] : "localhost");
		}
	}
	else
	{
		snprintf(localname, sizeof(localname), "%s", netname);
	}

	BW_INFO("===========>open sndsocket, localeth = %s\n", localname);

	bzero(&socket_ifreq, sizeof(struct ifreq));
	snprintf(socket_ifreq.ifr_name, sizeof(socket_ifreq.ifr_name), "%s", localname);

	if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
	{
		return -1;
	}


	if (ioctl(socket_fd, SIOCGIFADDR, &socket_ifreq) == -1)
	{
		close(socket_fd);
		return -1;
	}

	if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0)
	{
		close(socket_fd);
		return -1;
	}

	if (setsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, &sndbufsize, sizeof(sndbufsize)) < 0)
	{
		close(socket_fd);
		return -1;
	}

	if (setsockopt(socket_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
	{
		close(socket_fd);
		return -1;
	}

	struct ifreq ifr;
	memset(&ifr, 0x00, sizeof(ifr));
	strcpy(ifr.ifr_name, localname);
	setsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE, (char *) &ifr, sizeof(ifr));

	return socket_fd;
}

// if ip is valid, return 1
// else return 0
int enc_is_valid_ip(U32 ip)
{
	if (0 == ip)
	{
		return 1;
	}
	if (((ip & 0xFF) == 0) || ((ip & 0xFF) == 0xFF))
	{
		// all 0 or all 1
		return 0;
	}
	char buf[64];
	snprintf(buf, sizeof(buf), "%d.%d.%d.%d", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF);

    struct sockaddr_in sa;
    return (inet_pton(AF_INET, buf, &(sa.sin_addr)) == 1);
}

int enc_sendto(int fd, U8 *buf, int len, struct sockaddr_in *addr)
{
	int ret = 0;

	while (1) {
		ret = sendto(fd, buf, len, 0, (struct sockaddr *)addr, (socklen_t)(sizeof(struct sockaddr_in)));
		if (ret < 0) {
			if (errno == EINTR || errno == EAGAIN) {
				continue;
			}
			return ret;
		}
		return ret;
	}

	return ret;
}

int enc_get_map_ethid(int id)
{
	return id;
}

int enc_get_unmap_ethid(int id)
{
	return id;
}


void enc_dump_buf(int idx, U8* buf, int buf_len)
{
	static int	debug_dump = 0;
	static int	dump_finish = 0;
	static time_t debug_start_time = 0;
	char	check_debug[SZ_1K];
	char	dump_file[SZ_1K];

	if (!buf || buf_len <= 0)
	{
		return;
	}

	snprintf(check_debug, sizeof(check_debug), "%s/debug_%d", u::Util::module_folder(), idx);
	snprintf(dump_file, sizeof(dump_file), "%s/chl_%03d.ts", u::Util::module_folder(), idx);

	if (u::io::File::exists(check_debug))
	{
		if (!debug_dump)
		{
			// first time, empty dump file if exist
			remove(dump_file);
			debug_dump = 1;
			dump_finish = 0;
			debug_start_time = time(0);
			enc_log("begin dump channel %d stream [%s]....\n", idx, dump_file);
		}

		time_t t_now = time(0);
		if (t_now - debug_start_time <= 60*10)
		{
			//dump 2 minutes
			u::io::File::append(dump_file, (char*)buf, buf_len);
		}
		else
		{
			if (!dump_finish)
			{
				dump_finish = 1;
				enc_log("stop dump channel %d, [%s], size=%d\n", idx, dump_file, u::io::File::size(dump_file));
			}
		}
	}
	else
	{
		if (debug_dump)
		{
			debug_dump = 0;
			if (!dump_finish)
			{
				dump_finish = 1;
				enc_log("stop dump channel %d, [%s], size=%d\n", idx, dump_file, u::io::File::size(dump_file));
			}
		}
	}
}


void _enc_log(const char* format,...)
{
	va_list params;
	va_start(params, format);

	char _msg[SZ_2K];
	memset(_msg, 0, sizeof(_msg));
	vsnprintf(_msg, sizeof(_msg), format, params);
	char* p = _msg + strlen(_msg) - 1;
	while (*p == '\n' && p > _msg)
	{
		*p-- = 0;
	}
	snprintf(_msg + strlen(_msg), sizeof(_msg) - strlen(_msg), "\033[0;m");

	char ramFile[SZ_1K];

#ifndef BWFCGI
	snprintf(ramFile, sizeof(ramFile), "%s/hienc.log", HIENC_RAM_LOG_PATH);
#else
	if (!u::io::File::exists(HIENC_RAM_LOG_PATH))
	{
		u::io::File::mkdir(HIENC_RAM_LOG_PATH);
	}
	snprintf(ramFile, sizeof(ramFile), "%s/hienc_fcgi.log", HIENC_RAM_LOG_PATH);
#endif

	struct timeval tv = { 0 };
	gettimeofday(&tv, NULL);
	time_t _time = tv.tv_sec;
	struct tm occurs = { 0 };
	localtime_r(&_time, &occurs);

	if (u::io::File::size(ramFile) > SZ_1M * 10)
	{
		remove(ramFile);
	}

	if (g_logsem)
	{
		sem_wait(g_logsem);
	}
	FILE* fplog = fopen(ramFile, "a");
	if (fplog)
	{
		fprintf(fplog, "[%04d-%02d-%02d %02u:%02u:%02u.%03ld] %s\n",
				(1900 + occurs.tm_year), (1 + occurs.tm_mon), occurs.tm_mday,
				occurs.tm_hour, occurs.tm_min, occurs.tm_sec, tv.tv_usec / 1000,
				_msg);
		fclose(fplog);
	}
	if (g_logsem)
	{
		sem_post(g_logsem);
	}

#ifndef BWFCGI		//in cgi, could not printf to stdout
	fprintf(stderr, "[%04d-%02d-%02d %02u:%02u:%02u.%03ld] %s\n",
			(1900 + occurs.tm_year), (1 + occurs.tm_mon), occurs.tm_mday,
			occurs.tm_hour, occurs.tm_min, occurs.tm_sec, tv.tv_usec / 1000,
			_msg);
#endif
}

static int _running = 1;
int  enc_is_running()
{
	return _running;
}

void enc_sig_handler(int sig)
{
	if (SIGINT == sig) {
		_running = false;	// JUST FOR DEBUG
		signal(sig, SIG_DFL);
	}
}

int enc_hex2int(const char s[])
{
	enum {
		NO = 0,
		YES = 1
	};
    int hexdigit, i, inhex, n;

    i = 0;
    if(s[i] == '0')         //如果字符串以0x或0X开头，则跳过这两个字符
    {
        ++i;
        if(s[i] == 'x' || s[i] == 'X')
            ++i;
    }
    if (s[i]=='#') {
    	++i;
    }
    n = 0;
    inhex = YES;            //如果字符是有效16进制字符，则置inhex为YES
    for(; inhex == YES; ++i)
    {
        if(s[i] >= '0' && s[i] <= '9')
            hexdigit = s[i] - '0';
        else if(s[i] >= 'a' && s[i] <= 'f')
            hexdigit = s[i] - 'a' + 10;
        else if(s[i] >= 'A' && s[i] <= 'F')
            hexdigit = s[i] - 'A' + 10;
        else
            inhex = NO;
        if(inhex == YES)
            n = n * 16 + hexdigit;
    }

    return n;
}
